﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;


namespace TINF_lab09_7nsk
{
	public partial class FrmGlavna : Form
	{
		//inicijalizacija varijabli i grafičkog sučelja:
		#region Incijalizacija
		
		private int stanje = 0; //varijabla u kojoj pratimo u kojem se koraku rada nalazi komunikacijski sustav 
		private string poruka = ""; //nasumična poruka koju će generirati izvorište
		private bool imaSmetnji = false; //ovisno o odabiru korisnika preko grafičko sučelja ima ili nema smetnji u kanalu
		private bool greskaUKanalu = false; //signalizira se pojava greške u kanalu
		const int BROJ_SIMBOLA = 1000; //zadano je da izvorište treba generirati 1000 simbola

		public FrmGlavna()
		{
			InitializeComponent(); //inicijalizacija grafičkog sučelja; kod koji je automatski generirao Visual Studio, tj. Form Designer
			btnStop.Enabled = false; //inicijalno onemogućavamo korištenje gumba 'Stop'; omogućava se po pokretanju simulacije
		}
		
		#endregion

		//radnje koje se događaju u grafičkom sučelju (ispis podataka i sl.)
		#region GUI_Akcije 
		
		//odabir korisnika da li želi da budu smetnje u kanalu:
		private void chbSmetnje_CheckedChanged(object sender, EventArgs e)
		{
			if (chbSmetnje.Checked == true) imaSmetnji = true;
			else imaSmetnji = false;
		}
		
		//radnje koje se događaju pri kliku na gumb 'Start'; ovisi o tome u kojem koraku rada je trenutno komunikacijski sustav 
		private void btnStart_Click(object sender, EventArgs e)
		{
			switch (stanje)
			{
				case 0:
					poruka = GenerirajSimboleNaIzvoru();
					txtIzvor.Text = poruka;
					lblStatus.Text = "Korak 1/6: generiranje simbola na izvoru";
					btnStop.Enabled = true;
					chbSmetnje.Enabled = false;
					btnStart.Text = "Next >";
					stanje = 1;
				break;
				
				case 1:
					lblStatus.Text = "Korak 2/6: izvorno kodiranje Huffmanovim kodom";
					poruka = Huffman(poruka);
					txtHuffman.Text = poruka;
					stanje = 2;
				break;
				
				case 2:
					lblStatus.Text = "Korak 3/6: zaštitno kodiranje parnim paritetom";
					poruka = ParniParitet(poruka);
					txtParniParitet.Text = poruka;
					stanje = 3;
				break;

				case 3:
					lblStatus.Text = "Korak 4/6: prijenos poruke kanalom";
					poruka = Kanal(poruka);
					txtKanal.Text = poruka;
					stanje = 4;
				break;

				case 4:
					lblStatus.Text = "Korak 5/6: dekodiranje u dekoderu kanala";
					poruka = DekoderKanala(poruka);
					txtDekoderKanala.Text = poruka;
					stanje = 5;
				break;

				case 5:
					lblStatus.Text = "Korak 6/6: dekodiranje u dekoderu informacije";
					poruka = DekoderInformacije(poruka);
					txtDekoderInf.Text = poruka;
					btnStart.Text = "Finish";
					stanje = 6;
				break;

				case 6:
					Reset();
				break;

				default: break;
			}
		}

		private void btnStop_Click(object sender, EventArgs e)
		{
			Reset();
		}

		//povratak na početno stanje:
		private void Reset()
		{
			btnStart.Text = "Start";
			lblStatus.Text = "Kliknite 'Start' za pokretanje simulacije";
			btnStop.Enabled = false;
			chbSmetnje.Enabled = true;
			txtIzvor.Text = "";
			txtHuffman.Text = "";
			txtKanal.Text = "";
			txtParniParitet.Text = "";
			txtDekoderKanala.Text = "";
			txtDekoderInf.Text = "";
			greskaUKanalu = false;
			imaSmetnji = chbSmetnje.Checked;
			stanje = 0;
		}

		#endregion

		//o ovom dijelu je implementirano generiranje poruke, kodiranje huffmanovim kodom, kodiranje parnim paritetom,
		//prijenos poruke kanalom, dekodiranje kanala i dekodiranje informacije
		#region AlgoritmiGeneriranjaKodiranjaDekodiranja
		
		private string GenerirajSimboleNaIzvoru()
		{
			string simboli = "";
			Random rand = new Random();
			for (int i = 0; i < BROJ_SIMBOLA; i++)
			{
				//generiramo slučajni broj u rasponu [1, 10]:
				int slucajniBroj = rand.Next(1, 11);
				//ovisno o vrijednosti slučajnog broja dodajemo novi simoblu u poruku. Razmaci su stavljeni radi preglednosti.
				if (slucajniBroj < 5) simboli += "a ";
				else if (slucajniBroj < 6) simboli += "b ";
				else if (slucajniBroj < 7) simboli += "c ";
				else if (slucajniBroj < 9) simboli += "d ";
				else if (slucajniBroj < 11) simboli += "e ";
			}
			return simboli;
		}

		//kodiranje izvorne poruke Huffmanovim kodom. Kodne riječi unaprijed određene.
		private string Huffman(string poruka)
		{
			string kod = "";
			for (int i = 0; i < poruka.Length; i += 2)
			{
				switch (poruka[i])
				{
					case 'a': kod += "0 "; break;
					case 'b': kod += "101 "; break;
					case 'c': kod += "100 "; break;
					case 'd': kod += "111 "; break;
					case 'e': kod += "110 "; break;
				}
			}
			return kod;
		}

		//kodiranje parnim paritetom
		//nakon svakih 5 bitova se dodaje 1 paritetni bit
		private string ParniParitet(string poruka)
		{
			string kod = "";
			int brJedinica = 0;
			int brSimbola = 0;

			for (int i = 0; i < poruka.Length; i++)
			{
				if (poruka[i] != ' ')
				{
					brSimbola++;
					kod += poruka[i];
					if (poruka[i] == '1') brJedinica++;
					if ((brSimbola % 5 == 0) || (i == poruka.Length - 1))
					{
						if (brJedinica % 2 == 0) kod += "0";
						else kod += "1";
						brJedinica = 0;
					}
				}
			}
			return kod;
		}

		private string Kanal(string poruka)
		{
			string kod = "";
			if (imaSmetnji == false) return poruka;
		    else
		    //generiranje eventualne pogreške za svaki bit poruke; po istom principu kao i generiranje poruke na izvorištu
			{
		        Random rand = new Random();
				for (int i = 0; i < poruka.Length; i++)
				{
					if ((poruka[i] == '0') || (poruka[i] == '1'))
					{
						int slucajniBroj = rand.Next(1, 21);
						if (slucajniBroj == 1)
						{
							if (poruka[i] == '0') kod += "1";
							else kod += "0";
						}
						else kod += poruka[i];
					}
					else kod += poruka[i];
				}
		    }
			return kod;
		}
		
		private string DekoderKanala(string poruka)
		{
			//dekoder kanala ukoliko detektira grešku prekida prijenos poruke u sustavu - budući da grešku ne može ispraviti
			bool paranBrJedinica = true;
			for (int i = 0; i < poruka.Length; i++)
			{
				if ((poruka[i] == '1') && (paranBrJedinica == true)) paranBrJedinica = false;
				else if ((poruka[i] == '1') && (paranBrJedinica == false)) paranBrJedinica = true;
			}
			if (paranBrJedinica == true) return poruka.Substring(0, poruka.Length - 2);
			else
			{
				greskaUKanalu = true;
				return "Greška pri prijenosu.";
			}
		}

		private string DekoderInformacije(string poruka)
		{
			string simboli = "";
			if (greskaUKanalu == true) return "Greška pri prijenosu.";
			else
			//dekodiranje poruke
			//DKA sa 6 stanja i izlazom; skup ulaznih simbola je {0, 1}, dakako
			{
				int stanje = 0;
				for (int i = 0; i < poruka.Length; i++)
				{
					if ((poruka[i] == '0') || (poruka[i] == '1'))
					{
						char ulaz = poruka[i];
						if ((stanje == 0) && (ulaz == '0'))
						{
							stanje = 1;
							simboli += "a ";
						}
						else if ((stanje == 0) && (ulaz == '1'))
						{
							stanje = 2;
						}
						else if ((stanje == 1) && (ulaz == '0'))
						{
							stanje = 1;
							simboli += "a ";
						}
						else if ((stanje == 1) && (ulaz == '1'))
						{
							stanje = 2;
						}
						else if ((stanje == 2) && (ulaz == '0'))
						{
							stanje = 3;
						}
						else if ((stanje == 2) && (ulaz == '1'))
						{
							stanje = 4;
						}
						else if ((stanje == 3) && (ulaz == '0'))
						{
							stanje = 5;
							simboli += "c ";
						}
						else if ((stanje == 3) && (ulaz == '1'))
						{
							stanje = 5;
							simboli += "b ";
						}
						else if ((stanje == 4) && (ulaz == '0'))
						{
							stanje = 5;
							simboli += "e ";
						}
						else if ((stanje == 4) && (ulaz == '1'))
						{
							stanje = 5;
							simboli += "d ";
						}
						else if ((stanje == 5) && (ulaz == '0'))
						{
							stanje = 1;
							simboli += "a ";
						}
						else if ((stanje == 5) && (ulaz == '1'))
						{
							stanje = 2;
						}

					}
				}
			}
			return simboli;
		}

		#endregion
	}
}
